using System;
using System.Text;

using ColladaDotNet.OpenTKMapping;

using OpenTK;

namespace ColladaDotNet.SimpleMesh
{
	public class SimpleMeshGeometry
	{
		private readonly VertexBuffer vertexBuffer = new VertexBuffer();

		private IndexBufferMap subMeshes;

		private readonly BonesMap bones = new BonesMap();


		public SimpleMeshGeometry()
		{
			subMeshes = new IndexBufferMap(VertexBuffer);
		}

		public IndexBufferMap SubMeshes
		{
			get
			{
				return subMeshes;
			}
		}

		public VertexBuffer VertexBuffer
		{
			get
			{
				return vertexBuffer;
			}
		}

		public BonesMap Bones
		{
			get
			{
				return bones;
			}
		}

		public void Merge(SimpleMeshGeometry geometry)
		{
			foreach (MaterialLink subMesh in geometry.SubMeshes)
			{
				var buf = SubMeshes[subMesh.Key];
				var tri = subMesh.IndexBuffer.ToTriangleIndexBufferArray();
				for (int i = 0; i < tri.Length; i+=3)
				{
					buf.AddTriangle(geometry.VertexBuffer[tri[i]], geometry.VertexBuffer[tri[i+1]], geometry.VertexBuffer[tri[i+2]]);
				}
			}
		}
		public void Scale(double factor)
		{
			vertexBuffer.Scale(factor);
			bones.Scale(factor);
		}
		public void AddNode(Node node)
		{
			if (node.Controllers != null && node.Controllers.Count > 0)
			{
				AddControllerInstance(node.Controllers[0]);
			}
			else if (node.Geometries != null && node.Geometries.Count > 0)
			{
				AddGeometryInsance(node.Geometries[0]);
			}
		}

		private void AddGeometryInsance(GeometryInstance instance)
		{
			
		}

		private void AddControllerInstance(ControllerInstance instance)
		{
			if (instance.Skin != null)
			{
				if (instance.Skin.Geometry is Mesh)
					AddMeshSkin(instance.Skin, instance.Skin.Geometry as Mesh, instance.Sceleton);
			}
		}

		private void AddMeshSkin(Skin skin, Mesh mesh, Sceleton sceleton)
		{
			foreach (var meshItem in mesh.Items)
			{
				AddMeshItemSkin(skin, meshItem, sceleton);
			}
		}

		public void AddMeshItemSkin(Skin skin, MeshItem meshItem, Sceleton sceleton)
		{
			var indexBuffer = subMeshes[meshItem];
			for (ulong i=0; i<meshItem.NumPositions; )
			{
				Vertex[] tr = new Vertex[3];
				for (int j=0; j<3; ++j)
				{
					Vertex v = new Vertex();
					v.Position = V32V3D(meshItem.EvalPosition(i));
					v.TexCoord0 = new Mapping()
					              	{
					              			TexCoord = V32V3D(meshItem.EvalUV(i)),
					              			Tangent = V32V3D(meshItem.EvalTangent(i)),
					              			Binormal = V32V3D(meshItem.EvalBinormal(i)),
					              	};
					v.Normal = V32V3D(meshItem.EvalNormal(i));
					var w = skin.Weights[(int)meshItem.EvalWeightIndex(i)];
					if(w.Length > 0)
					{
						v.Bones = new VertexBone[w.Length];
						for (int i1 = 0; i1 < w.Length; i1++)
						{
							var weight = w[i1];
							v.Bones[i1] = new VertexBone() {Index = EvalBoneIndex(skin, sceleton, weight.Index), Weight = weight.Weight};
						}
					}

					tr[j] = v;
					++i;
				}
				indexBuffer.AddTriangle(tr[0], tr[1], tr[2]);
			}
		}

		private ulong EvalBoneIndex(Skin skin, Sceleton sceleton, ulong index)
		{
			var b = skin.Joints.GetName(index);
			var bone = sceleton.GetBone(b);
			var name = bone.Name;
			var res = Bones[name];
			if (res == null)
			{
				res = new Bone(name);
				res.InvBindMatrix = skin.BindShapeMatrix * skin.InvBindMatrix.GetMatrix(index);
				res.Matrix = bone.Matrix;
				Bones[name] = res;
			}
			return res.Index;
		}

		private static Vector3d V32V3D(Vector3 vector3)
		{
			return new Vector3d(vector3.X, vector3.Y, vector3.Z);
		}
	}
}
